;-----------------------------------------------------------------------;
; HOOKINTS.ASM								;
;									;
; Pictor, Version 1.51, Copyright (c) 1992-94 SoftCircuits.		;
; Redistributed by permission.						;
;-----------------------------------------------------------------------;
%	.MODEL	memmodel,c


	EXTRN	watcom_c atexit:PROC ;stdlib uses watcom_c- W. Jones
	EXTRN	c messagebox:PROC ;int 24h handler expects __cdecl to be used- W. Jones
	EXTRN   watcom_c sprintf:PROC ;stdlib uses watcom_c- W. Jones

	.DATA
	IF	@CodeSize
	EXTRN	_PL_helpfunc:FAR PTR ;FAR PTR originally- W. Jones*
old_helpfunc	DD	0
	ELSE
	EXTRN	_PL_helpfunc:PTR ;PTR originally- W. Jones*
old_helpfunc	DW	0
	ENDIF

	PUBLIC	_PL_breakflag
_PL_breakflag	DW	0

installed_flag	DB	0		;1 = handler is installed
registered_flag	DB	0		;1 = unhookints registered w/atexit

	IF	@DataSize		;Pointer to COLORSTRUCT for
error_colors	DD	0		; messagebox()
	ELSE
error_colors	DW	0
	ENDIF

MB_RETRYABORT	EQU	03h		;messagebox() flags
MAX_ERRORNUM	EQU	0Ch
error_00h	DB	"Attempted to write to write-protected disk",0
error_01h	DB	"Unknown unit (invalid drive number)",0
error_02h	DB	"Drive not ready (no diskette or drive door is open)",0
error_03h	DB	"Unknown command requested",0
error_04h	DB	"Data error (CRC)",0
error_05h	DB	"Length of requested structure invalid",0
error_06h	DB	"Seek error (move to requested cylinder failed)",0
error_07h	DB	"Non-MS-DOS disk",0
error_08h	DB	"Sector not found",0
error_09h	DB	"Printer out of paper",0
error_0Ah	DB	"Write fault",0
error_0Bh	DB	"Read fault",0
error_0Ch	DB	"General fault",0

error_table	LABEL	WORD
	DW	OFFSET error_00h,OFFSET error_01h,OFFSET error_02h
	DW	OFFSET error_03h,OFFSET error_04h,OFFSET error_05h
	DW	OFFSET error_06h,OFFSET error_07h,OFFSET error_08h
	DW	OFFSET error_09h,OFFSET error_0Ah,OFFSET error_0Bh
	DW	OFFSET error_0Ch

error_format	DB	"%s",0Ah,"%s",0
error_title	DB	"Error",0
device_error	DB	"Device error",0
disk_error	DB	"Error on drive "
drive		DB	"A:",0
buffer		DB	128 DUP (?)

old1B_handler	DD	0		;Storage for old interrupt handlers
old23_handler	DD	0
old24_handler	DD	0


	.CODE
;-----------------------------------------------------------------------;
; Ctrl-break interrupt hander.						;
;									;
; Usage:	This procedure cannot be called directly from C.	;
;-----------------------------------------------------------------------;
IFDEF	??version			;Turbo Assembler
int1B_handler	PROC	FAR
ELSE
int1B_handler	PROC FAR PRIVATE
ENDIF
	sti
	push	ax
	push	es
	mov	ax,0040h
	mov	es,ax
	IFDEF __WASM__
		and	BYTE PTR es:[0071h], 7Fh ;Original- "and BYTE PTR es:[0071h],NOT 80h". My guess is that clearing the flag was intended. -W. Jones
	ELSE
		and BYTE PTR es:[0071h],NOT 80h
	ENDIF
	mov	ax,@Data
	mov	es,ax
	mov	es:_PL_breakflag,1	;Set break flag
	pop	es
	pop	ax
	iret
int1B_handler	ENDP

;-----------------------------------------------------------------------;
; Ctrl-C interrupt handler.						;
;									;
; Usage:	This procedure cannot be called directly from C.	;
;-----------------------------------------------------------------------;
IFDEF	??version			;Turbo Assembler
int23_handler	PROC	FAR
ELSE
int23_handler	PROC FAR PRIVATE
ENDIF
	iret
int23_handler	ENDP

;-----------------------------------------------------------------------;
; This procedure becomes the new critical-error interrupt handler. This	;
; handler disables help by setting int (*_PL_helpfunc)() to NULL. Help	;
; must never be called from within an interrupt!			;
;									;
; Usage:	This procedure cannot be called directly from C.	;
;-----------------------------------------------------------------------;
IFDEF	??version			;Turbo Assembler
int24_handler	PROC	FAR
ELSE
int24_handler	PROC FAR PRIVATE
ENDIF
	sti
	push	bx ;Why isn't ax pushed? The order of pushing/popping
	push	cx ;must be a coincidence...- W. Jones
	push	dx
	push	si
	push	di
	push	bp
	push	ds
	push	es
	mov	dx,@Data		;ds = data segment
	mov	ds,dx
	IF	@CodeSize
	mov	bx,WORD PTR _PL_helpfunc[0]
	mov	WORD PTR old_helpfunc[0],bx
	mov	bx,WORD PTR _PL_helpfunc[2]
	mov	WORD PTR old_helpfunc[2],bx
	mov	WORD PTR _PL_helpfunc[0],0
	mov	WORD PTR _PL_helpfunc[2],0
	ELSE
	;mov	bx, WORD PTR _PL_helpfunc ;Added WORD PTR- W. Jones
	mov	bx, _PL_helpfunc
	mov	old_helpfunc,bx
	;mov	WORD PTR _PL_helpfunc,0 ;Added WORD PTR- W. Jones
	mov	_PL_helpfunc,0 
	ENDIF
	test	ah,80h			;Was the error disk-related?
	jnz	not_disk_error		;No
	add	al,'A'			;Else set drive letter
	mov	drive,al
	mov	dx,OFFSET disk_error	;Disk error message
	jmp	err_type_ready
not_disk_error:
	mov	dx,OFFSET device_error	;Device error message
err_type_ready:
	mov	bx,di			;Error code
	sub	bh,bh
	cmp	bx,MAX_ERRORNUM
	jna	good_errornum
	mov	bx,MAX_ERRORNUM
good_errornum:
	shl	bx,1
	add	bx,OFFSET error_table
	mov	bx,[bx]
	
	
;I found out the hard way that for variable-number argument functions,
;__watcall passes ALL parameters to the stack. In this scenario,
;no significant difference between __cdecl and __watcall exists.
;The below code can safely be ignored. It was kept for my
;reference- W. Jones.
	
;IFDEF __WASM__	;Compensate for 16-bit WATCOM calling convention.
				;__cdecl is right-to-left... __watcall is left-to-right
				;for register parameters, right-to-left for stack 
				;parameters- W. Jones.
;	IF	@DataSize		;sprintf(buffer,format,dx,bx);
;		push ds
;		push bx			;Pop the rightmost parameters onto the stack first
;		push ds			;for optimization purposes- W. Jones.
;		push dx			
;		mov dx, ds
;		mov ax, offset buffer	;Then place leftmost (remaining) parameters.
;		mov cx, ds				;into registers, order: [DX AX] [CX BX]- W. Jones.
;		mov bx, offset error_format
;	ELSE
;		;If data is near by default, all parameters fit in 
;		;registers, order: AX DX BX CX- W. Jones.
;		mov ax, offset buffer
;		mov cx, bx ;Move the error type pointer to cx- W. Jones.
;		mov bx, dx ;Move the error message pointer to bx- W. Jones.
;		mov dx, offset error_format
;	ENDIF
;	call	sprintf
;	IF	@DataSize
;	add	sp,8	;Get stack space back if far data was default and
;				;therefore stack was used in previous function call- W. Jones.
;	ELSE
;				;Stack wasn't used if data was near- W. Jones.
;	ENDIF
;ELSE	
	IF	@DataSize		;sprintf(buffer,format,dx,bx);
	push	ds
	push	bx
	push	ds
	push	dx
	push	ds
	mov	ax,OFFSET error_format
	push	ax
	push	ds
	mov	ax,OFFSET buffer
	push	ax
	ELSE
	push	bx
	push	dx
	mov	ax,OFFSET error_format
	push	ax
	mov	ax,OFFSET buffer
	push	ax
	ENDIF
	call	sprintf
	IF	@DataSize
	add	sp,16
	ELSE
	add	sp,8
	ENDIF
;ENDIF
	;messagebox maintains C-calling convention due to use in
	;this function, and to keep my life easy- W. Jones.
	IF	@DataSize		;messagebox(msg,wtitle,flags,&colors);
	push	WORD PTR error_colors[2]
	push	WORD PTR error_colors[0]
	mov	ax,MB_RETRYABORT
	push	ax
	push	ds
	mov	ax,OFFSET error_title
	push	ax
	push	ds
	mov	ax,OFFSET buffer
	push	ax
	ELSE
	push	error_colors
	mov	ax,MB_RETRYABORT
	push	ax
	mov	ax,OFFSET error_title
	push	ax
	mov	ax,OFFSET buffer
	push	ax
	ENDIF
	call	messagebox
	IF	@DataSize
	add	sp,14
	ELSE
	add	sp,8
	ENDIF
	IF	@CodeSize
	mov	bx,WORD PTR old_helpfunc[0]
	mov	WORD PTR _PL_helpfunc[0],bx
	mov	bx,WORD PTR old_helpfunc[2]
	mov	WORD PTR _PL_helpfunc[2],bx
	ELSE
	mov	bx,old_helpfunc
	;IFNDEF __WASM__
		;mov	WORD PTR _PL_helpfunc,bx ;Added WORD PTR- W. Jones
	;ELSE
	mov _PL_helpfunc,bx
	;ENDIF
	ENDIF
	cmp	ax,01h			;Did user give 'retry' response?
	je	end_int24_handler	;Yes
	mov	ax,03h			;Else fail current system call
end_int24_handler:
	pop	es
	pop	ds
	pop	bp
	pop	di
	pop	si
	pop	dx
	pop	cx
	pop	bx
	iret
int24_handler	ENDP

	PUBLIC	unhookints
;-----------------------------------------------------------------------;
; Removes the interrupt handlers installed by hookints and re-installs	;
; the original interrupt vectors.					;
;									;
; Usage:	int unhookints(void);					;
; Returns:	0 = success, -1 = error					;
;-----------------------------------------------------------------------;
IFDEF __WASM__
	unhookints PROC watcom_c ;See pictor.h declaration for reasoning using watcall here- W. Jones. 
	;push ax
	;push bx ;All registers non-volatile in __watcall (excepting those used for return values)- W. Jones.
	push dx
ELSE
	unhookints	PROC
ENDIF
	mov	ax,-1			;Return error if our handlers
	cmp	installed_flag,0	; are not installed
	je	end_unhookints
	push	ds			;Preserve ds
	push	ds			;Use es to access data segment
	pop	es
	mov	ax,251Bh		;Restore original interrupt 1Bh
	lds	dx,es:old1B_handler	; vector
	int	21h
	mov	ax,2523h		;Restore original interrupt 23h
	lds	dx,es:old23_handler	; vector
	int	21h
	mov	ax,2524h		;Restore original interrupt 24h
	lds	dx,es:old24_handler	; vector
	int	21h
	pop	ds
	mov	installed_flag,0	;Indicate we're no longer installed
	sub	ax,ax
end_unhookints:
IFDEF __WASM__
	pop dx
	;pop bx ;All registers non-volatile in __watcall (excepting those used for return values)- W. Jones. 
	;pop ax ;atexit() will (probably- not checked) crash and burn without this after return- W. Jones.
ENDIF
	ret
unhookints	ENDP

	PUBLIC	hookints
;-----------------------------------------------------------------------;
; Installs custom interrupt handlers for Ctrl-C, Ctrl-break and		;
; critical error interrupts, and registers unhookints with atexit.	;
; colors argument specifies color used by messagebox for critical	;
; errors.								;
;									;
; Usage:	int hookints(COLORSTRUCT *colors);			;
; Returns:	0 = success, -1 = error.				;
;-----------------------------------------------------------------------;
hookints	PROC colors:PTR ;BYTE commented out- W. Jones.
	mov	ax,-1			;Return error if we're already
	cmp	installed_flag,1	; installed
	jne	not_installed
	jmp	end_hookints
not_installed:
	IF	@DataSize
	mov	ax,WORD PTR colors[0]
	mov	WORD PTR error_colors[0],ax
	mov	ax,WORD PTR colors[2]
	mov	WORD PTR error_colors[2],ax
	ELSE
	mov	ax,colors
	mov	error_colors,ax
	ENDIF
	mov	ax,351Bh		;Get old interrupt 1Bh vector
	int	21h
	mov	WORD PTR old1B_handler[0],bx
	mov	WORD PTR old1B_handler[2],es
	push	ds			;Set new interrupt 1Bh vector
	mov	ax,251Bh
	push	cs
	pop	ds
	mov	dx,OFFSET int1B_handler
	int	21h
	pop	ds
	mov	ax,3523h		;Get old interrupt 23h vector
	int	21h
	mov	WORD PTR old23_handler[0],bx
	mov	WORD PTR old23_handler[2],es
	push	ds			;Set new interrupt 23h vector
	mov	ax,2523h
	push	cs
	pop	ds
	mov	dx,OFFSET int23_handler
	int	21h
	pop	ds
	mov	ax,3524h		;Get old interrupt 24h vector
	int	21h
	mov	WORD PTR old24_handler[0],bx
	mov	WORD PTR old24_handler[2],es
	push	ds			;Set new interrupt 24h vector
	mov	ax,2524h
	push	cs
	pop	ds
	mov	dx,OFFSET int24_handler
	int	21h
	pop	ds
	mov	installed_flag,1	;Indicate we're installed
	mov	ax,0
	cmp	registered_flag,1	;Are we already registered w/atexit?
	je	end_hookints		;Yes, done
	mov	registered_flag,1
	
	IFDEF __WASM__ ;Compensate for 16-bit WATCOM calling convention- W. Jones
		IF	@CodeSize		;atexit(unhookints)
			mov dx, cs
			mov	ax, OFFSET unhookints
		ELSE
			mov	ax, OFFSET unhookints
		ENDIF
		call	atexit	
	ELSE
		IF	@CodeSize		;atexit(unhookints)
		push	cs
		mov	ax,OFFSET unhookints
		push	ax
		ELSE
		mov	ax,OFFSET unhookints
		push	ax
		ENDIF
		call	atexit
		IF @CodeSize
		add	sp,4
		ELSE
		inc	sp
		inc	sp
		ENDIF
	ENDIF
	
	or	ax,ax			;Was atexit() successful?
	jz	end_hookints		;Yes, return 0
	call	unhookints		;Else uninstall handler
	mov	registered_flag,0
	mov	ax,-1			;Return error
end_hookints:
	ret
hookints	ENDP

	END
